/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import path from 'path';

import { HvigorConfigLoader, hvigorCore, Module, ToolingModelBean } from '@ohos/hvigor';

import { ModulePathInfoIml } from '../../../common/iml/module-path-info-iml.js';
import { BuildArtifactConst, BuildDirConst } from '../../../const/build-directory-const.js';
import { CommonConst, DefaultTargetConst } from '../../../const/common-const.js';
import { CodeType } from '../../../enum/code-type-enum.js';
import { CompileModeEnum } from '../../../enum/compile-mode-enum.js';
import { ModuleType } from '../../../enum/module-type-enum.js';
import { CoreModuleModelImpl } from '../../../model/module/core-module-model-impl.js';
import { ModuleModel } from '../../../model/module/module-model.js';
import { ProjectModel } from '../../../model/project/project-model.js';
import { ProjectModelImpl } from '../../../model/project/project-model-impl.js';
import { AppPlugin } from '../../../plugin/app-plugin.js';
import { AbstractModulePlugin } from '../../../plugin/common/abstract-module-plugin.js';
import { OhosPluginId } from '../../../plugin/common/ohos-plugin-id.js';
import { buildOptionManager } from '../../../project/build-option/build-mode-manager.js';
import { Dependency } from '../../../project/dependency/core/dependency-interface.js';
import { CompileResourcesUtil } from '../../../utils/compile-resources-util.js';
import { InjectUtil } from '../../../utils/inject-util.js';
import { OhosLogger } from '../../../utils/log/ohos-logger.js';
import { strMapToObj } from '../../../utils/map-util.js';
import { TaskNames } from '../../common/task-names.js';
import { CompileNode } from '../../compile-node.js';
import { ModuleTargetData } from '../../data/hap-task-target-data.js';
import { ModuleTaskService } from '../../service/module-task-service.js';
import { computeTargetTaskName, TargetTaskService } from '../../service/target-task-service.js';
import { BuildOpt } from '../../../options/build/build-opt.js';
import { findTargetProduct } from '../../../common/find-target-product.js';
import { harModuleBuildProfileLoader } from '../../../utils/loader/file/har-module-build-profile-loader.js';
import { moduleBuildProfileLoader } from '../../../utils/loader/file/module-build-profile-loader.js';

const _log = OhosLogger.getLogger('OhosModuleModelBean');

const ohosTestTargetName = DefaultTargetConst.OHOS_TEST_TARGET;
const moduleType2IdMap = new Map<ModuleType, string>([
  [ModuleType.Har, OhosPluginId.OHOS_HAR_PLUGIN],
  [ModuleType.Entry, OhosPluginId.OHOS_HAP_PLUGIN],
  [ModuleType.Feature, OhosPluginId.OHOS_HAP_PLUGIN],
  [ModuleType.Shared, OhosPluginId.OHOS_HSP_PLUGIN],
]);

/**
 * OhosProject中的Module模块的toolModel数据模型
 *
 * @since 2022/2/19
 */
export class OhosModuleModelBean implements ToolingModelBean {
  modelId = 'ohos-module';

  private readonly _isFaMode;
  private readonly _module: Module;
  private readonly _projectModel: ProjectModel;
  private readonly _moduleModel: ModuleModel;
  private readonly _moduleTaskService: ModuleTaskService;
  private readonly _targetDataSet: Set<[ModuleTargetData, boolean]>;
  private readonly _isHarModule: boolean;
  private readonly _targetTaskServices: TargetTaskService[];
  private readonly _moduleType: string;

  constructor(module: Module, isFaMode: boolean) {
    this._isFaMode = isFaMode;
    this._module = module;

    const project = module.getProject();
    const appPlugin = project.getPluginById(OhosPluginId.OHOS_APP_PLUGIN) as AppPlugin;
    this._projectModel = appPlugin.getProjectModel() as ProjectModelImpl;
    this._moduleModel = this._projectModel.getModuleModelByName(this._module.getName())!;
    this._moduleType = this._moduleModel.getModuleType();
    this.modelId = this.modelId.concat('-').concat(module.getName());

    this._isHarModule = this._moduleType === ModuleType.Har;
    const pluginId = moduleType2IdMap.get(this._moduleModel.getModuleType()!)!;
    const plugin = module.getPluginById(pluginId) as AbstractModulePlugin;
    if (!plugin) {
      _log.printErrorExit('THE_%S_PLUGIN_WAS_NOT_FOUND_WHEN_INITIALIZING_THE_%S_MODULE', [pluginId, module.getName()]);
    }
    this._moduleTaskService = plugin.getTaskService()!;
    this._targetTaskServices = plugin.getNeedExecTargetServiceList();
    this._targetDataSet = this._moduleTaskService.getTargetDataSet();
  }

  private computeCommonInfo(info: Map<string, any>) {
    info.set('SELECT_TARGET', DefaultTargetConst.DEFAULT_TARGET);
    const buildDir = path.resolve(this._module.getNodeDir(), CommonConst.BUILD);
    const customBuildDir = InjectUtil.getBuildCacheParentDir(
      buildDir,
      path.join(hvigorCore.getProject()!.getName(), buildDir.substring(hvigorCore.getProject()!.getNodeDir().length)),
    );
    // 设置module的编译目录信息
    info.set('MODULE_BUILD_DIR', path.resolve(customBuildDir));
  }

  private computeBuildOption(info: Map<string, any>) {
    const buildOption: Record<string, BuildOpt> = {};
    this._targetTaskServices.forEach((targetTaskService) => {
      const buildOptionKey = findTargetProduct(this._projectModel!).name.concat('-').concat(targetTaskService.getTargetData().getTargetName());
      buildOption[buildOptionKey] = targetTaskService.getBuildOption();
    });
    info.set('BUILD_OPTION', buildOption);
    if (this._isHarModule) {
      info.set('BUILD_PROFILE_OPT', harModuleBuildProfileLoader.getBuildProfile(this._moduleModel.getName()));
    } else {
      info.set('BUILD_PROFILE_OPT', moduleBuildProfileLoader.getBuildProfile(this._moduleModel.getName()));
    }
  }

  private computePathInfo(info: Map<string, any>) {
    const targets = new Map();
    this._targetDataSet.forEach((value) => {
      const targetData: ModuleTargetData = value[0];
      const targetName = targetData.getTargetName();

      const targetInfoMap = new Map();
      const modulePathInfo = targetData.getPathInfo();
      const moduleModel = this._moduleTaskService.getModuleModel();
      const cachePath = this.getCachePath(modulePathInfo, targetData, moduleModel);

      const workerLoader = path.resolve(modulePathInfo.getIntermediatesLoaderPath(), BuildArtifactConst.LOADER_JSON);
      const harList: Dependency[] = this._moduleTaskService.getHarDependencies();
      const harResourcesMap = new Map();
      const moduleResourcesMap = new Map();
      harList.forEach((har, seq) => {
        const harPath = path.resolve(har.getDependencyRootPath(), 'src', 'main', BuildDirConst.RESTOOL_BUILD_RESOURCES);
        const compileDirName = CompileResourcesUtil.getCompileTargetFolderBySeq(BuildDirConst.HAR_COMPILED, seq);

        // key为harPath，value为restool编译中间文件夹名称
        harResourcesMap.set(harPath, compileDirName);
      });
      const buildPath = {
        OUTPUT_PATH: modulePathInfo.getModuleBuildOutputPath(),
        INTERMEDIA_PATH: modulePathInfo.getModuleBuildIntermediates(),
        JS_ASSETS_PATH: modulePathInfo.getInterMediatesLoaderOutPath(),
        JS_LITE_ASSETS_PATH: modulePathInfo.getInterMediatesLoaderOutLitePath(),
        RES_PATH: path.resolve(modulePathInfo.getIntermediatesRes(), this.getFirstEntryName()),
        RES_PROFILE_PATH: modulePathInfo.getIntermediatesResProfilePath(),
        ETS_SUPER_VISUAL_PATH: cachePath.etsLoaderCachePath,
        JS_SUPER_VISUAL_PATH: cachePath.aceLoaderCachePath,
        WORKER_LOADER: workerLoader,
        MANIFEST_JSON: modulePathInfo.getIntermediatesLegacyManifestJson(),
        OUTPUT_METADATA_JSON:
          this._moduleType === ModuleType.Shared ? modulePathInfo.getIntermediatesHspOutputMetadata() : modulePathInfo.getIntermediatesOutputMetadata(),
        SOURCE_MAP_DIR: modulePathInfo.getInterMediatesSourceMapDirPath(),
      };

      targetInfoMap.set('SOURCE_ROOT', moduleModel?.getSourceRootByTargetName(targetName));
      const resArr: string[] = [];
      resArr.push(<string>moduleModel?.getSourceSetByTargetName(targetName).getTargetResPath());

      resArr.forEach((dir, seq) => {
        const resAbsolutePath = path.isAbsolute(dir) ? dir : path.resolve(moduleModel.getProjectDir(), dir);
        const compileDirName = CompileResourcesUtil.getCompileTargetFolderBySeq(BuildDirConst.MODULE_COMPILED, seq);
        moduleResourcesMap.set(resAbsolutePath, compileDirName);
      });

      const targetBuildOption = buildOptionManager.getTargetBuildOption(this._moduleModel.getName(), targetName);
      targetInfoMap.set('RESOURCES_PATH', resArr);
      targetInfoMap.set('BUILD_PATH', buildPath);
      targetInfoMap.set('BUILD_OPTION', {
        debuggable: targetBuildOption.debuggable ?? true,
      });
      targets.set(targetName, targetInfoMap);
    });
    info.set('TARGETS', targets);
  }

  private getCachePath(pathInfo: ModulePathInfoIml, targetData: ModuleTargetData, moduleModel: CoreModuleModelImpl) {
    const targetName = targetData.getTargetName();
    let targetService = this._targetTaskServices.find((targetService) => targetService.getTargetData().getTargetName() === targetName);
    if (targetName === ohosTestTargetName) {
      targetService = this._targetTaskServices.find((targetService) => targetService.getTargetData().getTargetName() !== targetName);
    }

    const etsWithRollup: boolean = targetService ? targetService.getSdkInfo().hasRollUpPluginInEtsLoader : true;
    let faName = etsWithRollup ? TaskNames.LegacyFATask.COMPILE_ARK.name : TaskNames.LegacyFATask.COMPILE_NODE.name;
    let stageName = etsWithRollup ? (this._isHarModule ? TaskNames.Task.HAR_COMPILE.name : TaskNames.Task.COMPILE_ARK.name) : TaskNames.Task.COMPILE_NODE.name;
    if (targetName === ohosTestTargetName) {
      faName = etsWithRollup ? TaskNames.LegacyFATask.OHOS_TEST_ARK_COMPILE.name : TaskNames.LegacyFATask.OHOS_TEST_COMPILE_NODE.name;
      stageName = etsWithRollup ? TaskNames.Task.OHOS_TEST_ARK_COMPILE.name : TaskNames.Task.OHOS_TEST_COMPILE_NODE.name;
    }
    const compileNodeTaskPrefix: string = this._isFaMode ? faName : stageName;

    const etsCompileTaskName: string = computeTargetTaskName(
      targetData.getTargetName(),
      CompileNode.getCompileNodeTaskName(CodeType.ETS, compileNodeTaskPrefix),
    );
    const etsLoaderCachePath = path.resolve(pathInfo.getModuleBuildCachePath(), etsCompileTaskName, `${moduleModel.getCompileMode(targetData.getProduct())}`);

    const jsCompileTaskName: string = computeTargetTaskName(targetData.getTargetName(), CompileNode.getCompileNodeTaskName(CodeType.JS, compileNodeTaskPrefix));
    const aceLoaderCachePath = path.resolve(pathInfo.getModuleBuildCachePath(), jsCompileTaskName, `${CompileModeEnum.JS_BUNDLE}`);
    return { etsLoaderCachePath: etsLoaderCachePath, aceLoaderCachePath: aceLoaderCachePath };
  }

  private getFirstEntryName(): string {
    const relatedEntryModules = this._moduleTaskService.getRelatedEntryModules();
    const moduleType = this._moduleTaskService.getModuleModel().getModuleType();
    if (!ModuleTaskService.checkEntryModules(moduleType, relatedEntryModules) || !this._moduleTaskService.isFaMode()) {
      return '';
    }
    return relatedEntryModules![0];
  }

  private computeBuildDirInfo(info: Map<string, any>) {
    const buildCacheDir =
      hvigorCore.getExtraConfig().get(CommonConst.BUILD_CACHE_DIR) ??
      HvigorConfigLoader.getInstance().getPropertiesConfigValue<string>(CommonConst.BUILD_DIR_KEY);
    info.set('BUILD_CACHE_DIR', buildCacheDir || '');
  }

  buildModel(): string | undefined {
    const info: Map<string, any> = new Map<string, any>();

    this.computeCommonInfo(info);
    this.computePathInfo(info);
    this.computeBuildOption(info);
    this.computeBuildDirInfo(info);

    return strMapToObj(info);
  }
}
